package com.slz3.business.frame.util;

import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.SerializationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.io.Closeable;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisClient {

    public static RedisTemplate redisTemplate;

    /**
     * 静态注入RedisTemplate
     */
    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisClient.redisTemplate = redisTemplate;
    }

    private static final long DefaultTime = Integer.valueOf(600000);


    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            if (value instanceof String) {
                //字符串类型值
                redisTemplate.opsForValue().set(key, value.toString());
            } else if (value instanceof List) {
                //list类型值
                redisTemplate.opsForList().rightPush(key, value);
            } else if (value instanceof Set) {
                //set类型值
                redisTemplate.opsForSet().add(key, value);
            } else if (value instanceof Map) {
                //map类型值
                redisTemplate.opsForHash().putAll(key, (Map) value);
            } else {
                //其他对象类型
                redisTemplate.opsForValue().set(key, value);
            }
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            if (set(key, value)) {
                redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            }
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 单个哈希 添加
     *
     * @param key
     * @param hashKey
     * @param value
     */
    public void hmSet(String key, Object hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * 多个哈希 添加
     * @param key
     */
    public void hmSetMap(String key,Map<Object,Object> data) {
        redisTemplate.opsForHash().putAll(key, data);
    }

    /**
     * 有序集合添加
     *
     * @param key
     * @param value
     * @param score
     */
    public void zAdd(String key, Object value, double score) {
        redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public <T> T get(final String key) {
        return (T) redisTemplate.opsForValue().get(key);
    }

    /**
     * 哈希获取数据
     *
     * @param key
     * @param hashKey
     * @return
     */
    public Object hmGet(String key, Object hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }


    /**
     * 获取Map集合数据
     *
     * @param key
     * @return
     */
    public Map getMap(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 列表获取
     *
     * @param key
     * @param start 开始下标
     * @param end   结束下标
     * @return
     */
    public List getListStartEnd(String key, int start, int end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 集合获取
     *
     * @param key
     * @return
     */
    public Set<Object> getSet(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 有序集合获取
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<Object> getSetByRange(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * 查询key的剩余存活时间
     *
     * @param key
     * @return
     */
    public long getKeyExpireTime(String key) {
        return redisTemplate.getExpire(key);
    }



    /**
     * 查询key的剩余存活时间
     *
     * @param key
     * @return
     */
    public Set kes(String key) {
        return redisTemplate.keys(key);
    }

    /**
     * 设置key的剩余存活时间
     *
     * @param key
     * @param timeOut
     * @return
     */
    public boolean setKeyExpireTime(String key, int timeOut) {
        long times = 0;
        if (timeOut > 0) {
            times = timeOut * 60;
        } else {
            times = DefaultTime;
        }
        return redisTemplate.expire(key, times, TimeUnit.SECONDS);
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern
     */
    public void removePattern(final String pattern) {
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 增加或减少key
     * @param increment
     * @param key
     * @return 结果值（增加之后的值）
     */
    public Long incrAndDecr(Object key,long increment) {
        return redisTemplate.opsForValue().increment(key,increment);
    }

    /**
     * 增加或减少key
     * @param increment
     * @param key
     * @return 结果值（增加之后的值）
     */
    public Double incrAndDecr(Object key,double increment) {
        return redisTemplate.opsForValue().increment(key,increment);
    }

    /**
     * 向list中右侧添加数据
     * @param value
     * @param key
     */
    public boolean rpush(String key,Object value){
        boolean result = false;
        try {
            redisTemplate.opsForList().rightPush(key,value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 向list中左侧添加数据
     * @param value
     * @param key
     */
    public boolean lpush(String key,Object value){
        boolean result = false;
        try {
            redisTemplate.opsForList().leftPush(key,value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 从另一个右侧弹出
     * @param key
     */
    public Object rightPop(Object key){
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 从另一个左侧弹出
     * @param key
     */
    public Object leftPop(Object key){
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 右侧弹出从另一个list左侧添加
     * @param sourceKey
     * @param destinationKey
     */
    public Object rightPopAndLeftPush(Object sourceKey,Object destinationKey){
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,destinationKey);
    }

    /**
     * 右侧弹出从另一个list左侧添加
     * @param sourceKey
     * @param destinationKey
     * @param timeout
     */
    public Object rightPopAndLeftPush(Object sourceKey,Object destinationKey, long timeout){
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,destinationKey,timeout,TimeUnit.SECONDS);
    }


    /**
     * 通过keyList批量查询 使用管道一次连接 而mget要多次
     * @param:keyList
     */
    public List<Object> getList(List<String> keyList){
        return redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                for (String key:keyList){
                    byte[] keys =rawKey(key);
                    deserializeHashValues(connection.mGet(keys));
                }
                return null;
            }
        });
    }

    byte[] rawKey(Object key) {
        Assert.notNull(key, "non null key required");
        if (redisTemplate.getKeySerializer() == null && key instanceof byte[]) {
            return (byte[]) key;
        }
        return redisTemplate.getKeySerializer().serialize(key);
    }

    <T> List<T> deserializeHashValues(List<byte[]> rawValues) {
        if (redisTemplate.getHashValueSerializer() == null) {
            return (List<T>) rawValues;
        }
        return SerializationUtils.deserialize(rawValues, redisTemplate.getHashValueSerializer());
    }

    /**
     * 发送消息
     * @param:channel
     * @param message
     */
    public void sendMesssage(String channel,Object message){
        redisTemplate.convertAndSend(channel,message);
    }

    /**
     *获取列表元素个数 空列表为0
     * @param:key
     *
     */
    public int getListLen(String key){
        Long size = redisTemplate.opsForList().size(key);
        return size.intValue();
    }
    /**
     * 根据 key  field 模糊查询 hash key数据
     * @param:key
     */
    public List<Map<String,Object>>  getLikeList(String key,String field){
        BoundHashOperations boundHashOps = redisTemplate.boundHashOps(key);
        ScanOptions options = ScanOptions.scanOptions().match(field.concat("*")).build();
        List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
        Closeable closeable = redisTemplate.executeWithStickyConnection(new RedisCallback() {
            @Override
            public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                Cursor<Map.Entry<byte[], byte[]>> entryCursor = redisConnection.hScan(key.getBytes(), options);
                for (Cursor<Map.Entry<byte[], byte[]>> it = entryCursor; it.hasNext(); ) {
                    Map<String,Object> map = new HashMap<String,Object>();
                    Map.Entry<byte[], byte[]> next = it.next();
                    map.put(new String(next.getKey()),new Gson().toJson(new String(next.getValue())));
                    list.add(map);
                }
                return entryCursor;
            }
        });
        return list;
    }

    /**
     * 删除 hash 列表某一个属性值
     * @param key:key   field: 属性字段
     */
    public void remove(String key,String field){
        redisTemplate.opsForHash().delete(key, field);
    }
    /**
     * 删除 hash 列表某一个属性值
     * @param key:key   field: 属性字段集合
     */
    public void remove(String key,List<String> fields){
        for (String field:fields) {
            redisTemplate.opsForHash().delete(key,field);
        }
    }
    /**
     *获取zset 集合的长度
     * @param key zset key
     */
    public int getZsetLen(String key){
        Long size = redisTemplate.opsForZSet().size(key);
        return size.intValue();
    }

    /**
     *获取set 集合的长度
     * @param key set key
     */
    public int getSetLen(String key){
        Long size = redisTemplate.opsForSet().size(key);
        return size.intValue();
    }
    /**
     * 删除set集合 某一个值
     * @param key :setkey
     * @param field:set 里面的值
     */
    public void removeFromSet(String key,String field){
        redisTemplate.opsForSet().remove(key,field);
    }

    /**
     * 删除Zset集合 某一个值
     * @param key :setkey
     * @param field:set 里面的值
     */
    public void removeFromZSet(String key,String field){
        redisTemplate.opsForZSet().remove(key,field);
    }

    /**
     * 删除list集合 某一个值
     * @param key :listkey
     * @param count
     *          count > 0 : 从表头开始向表尾搜索，移除与 value 相等的元素，数量为 count 。
     *          count < 0 : 从表尾开始向表头搜索，移除与 value 相等的元素，数量为 count 的绝对值。
     *          count = 0 : 移除表中所有与 value 相等的值。
     * @param value:list 里面的值
     */
    public void removeFromList(String key,long count,Object value){
        redisTemplate.opsForList().remove(key,count,value);
    }

    /**
     * 获取所有元素的类型
     * @param keyList key 集合
     * @return hashMap  string:key   DataType :key类型
     */
    public LinkedHashMap<String, DataType> getKeyTypes (List<String> keyList) {
        LinkedHashMap<String, DataType> hash = new LinkedHashMap<>();
        for (String key : keyList) {
            DataType type = redisTemplate.type(key);
            hash.put(key, type);
        }
        return hash;
    }
    /**
     * 根据key获取list集合
     */
    public List<Object> getListByKey (List<String> keyList){
        LinkedHashMap<String, DataType> keyTypes = getKeyTypes(keyList);
        List<Object> result = redisTemplate.executePipelined(new RedisCallback<List<Object>>() {
            @Override
            public List<Object> doInRedis(RedisConnection connection)
                    throws DataAccessException {
                for(String key:keyTypes.keySet()){
                    byte[] keyb = redisTemplate.getStringSerializer().serialize(
                            (key));
                    DataType dataType = keyTypes.get(key);
                    switch(dataType){
                        case HASH : connection.hGetAll(keyb);
                            break;
                        case STRING:connection.get(keyb);
                            break;
                        case LIST:connection.lRange(keyb,0,-1);
                            break;
                        case SET:connection.sMembers(keyb);
                            break;
                        case ZSET:connection.zRangeByLex(keyb);
                            break;
                    }
                }
                return null;
            }
        });
        return ((List<Object>) result);
    }

}